package name.luojun.DBhelper.ORM;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import name.luojun.DBhelper.ORM.annotation.UniqueConstraint;
import name.luojun.DBhelper.ORM.drivers.DriverSupport;
import name.luojun.DBhelper.ORM.drivers.OracleDriver;
import name.luojun.DBhelper.ORM.entity.Column;
import name.luojun.DBhelper.ORM.entity.PrimaryKey;
import name.luojun.DBhelper.ORM.entity.Table;

import org.hibernate.MappingException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.util.ClassUtils;
@SuppressWarnings("rawtypes")
public class AnnotationTableManager extends NamedParameterJdbcDaoSupport {
	private static AnnotationTableManager instance = null;
	private String[] packagesToScan;
	private String dialect = "oracle";
	
	private List<Class> annotatedClass = new ArrayList<Class>();
	private DriverSupport driver;
	@Override
	protected void checkDaoConfig() {
		super.checkDaoConfig();
		Connection connection = this.getConnection();
		if (dialect.equalsIgnoreCase("oracle")) {
			driver = new OracleDriver(connection);
		} else {
			this.releaseConnection(connection);
			logger.error(dialect + " dialect not support!");
			return;
		}
		try {
			this.initialize();
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
		}
		this.updateSchema();
		this.releaseConnection(driver.releaseConnection());
		instance = this;
	}
	private void updateSchema() {
		for (Class clazz : annotatedClass) {
			Table table = this.ResolveTableAnnotation(clazz);
			this.updateTable(table);
		}
	}
	private void updateTable(Table table) {
		Table before = driver.getTable(table.getName());
		if (table.getColumns().size() > 0) {
			if (before == null || before.getColumns().size() == 0) {
				driver.createTable(table);
			} else {
				this.updateColumns(before, table);
				this.updatePrimaryKey(before, table);
				this.updateUniqueKey(before, table);
				this.updateForeignKey(before, table);
				this.updateIndex(before, table);
			}
		}
	}
	private void updateIndex(Table before, Table table) {
	}
	private void updateForeignKey(Table before, Table table) {
	}
	private void updateUniqueKey(Table before, Table table) {
	}
	private void updateColumns(Table before, Table table) {
		Map<String, Column> cols = table.getColumns();
		Map<String, Column> beforecols = before.getColumns();
		for (Entry<String, Column> entry : cols.entrySet()) {
			Column colbefore = beforecols.get(entry.getKey());
			if (colbefore == null) {
				driver.addColumn(table, colbefore);
			} else if (!driver.equals(colbefore, entry.getValue())) {
				driver.modifyColumn(table, entry.getValue(), colbefore);
			}
		}
	}
	private void updatePrimaryKey(Table before, Table table) {
		PrimaryKey prinow = table.getPrimaryKey();
		PrimaryKey pribefore = before.getPrimaryKey();
		if (prinow != null) {
			if (pribefore == null) {
				driver.addPrimayKey(table);
			} else if (!driver.equals(pribefore, prinow)) {
				driver.dropPrimayKey(before);
				driver.addPrimayKey(table);
			}
		}
	}
	private void initialize() throws IOException, ClassNotFoundException {
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		if (packagesToScan != null)
			try {
				for (String pkg : packagesToScan) {
					String pattern = (new StringBuilder()).append("classpath*:").append(ClassUtils.convertClassNameToResourcePath(pkg))
							.append("/**/*.class").toString();
					Resource resources[] = resourcePatternResolver.getResources(pattern);
					MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
					for (Resource resource : resources) {
						if (!resource.isReadable())
							continue;
						MetadataReader reader = readerFactory.getMetadataReader(resource);
						String className = reader.getClassMetadata().getClassName();
						if (matchesEntityTypeFilter(reader, readerFactory)) {
							annotatedClass.add(Class.forName(className));
						}
					}
				}
			} catch (IOException ex) {
				throw new MappingException("Failed to scan classpath for unlisted classes", ex);
			}
	}
	public Table ResolveTableAnnotation(Class clazz) {
		Table result=new Table();
		result.setClazz(clazz);
		boolean casesensitive = driver.isCaseSensitive();
		Annotation[] classanno = clazz.getDeclaredAnnotations();
		for (Annotation annotation : classanno) {
			Class type = annotation.annotationType();
			if (type == name.luojun.DBhelper.ORM.annotation.Table.class) {
				name.luojun.DBhelper.ORM.annotation.Table tableanno = (name.luojun.DBhelper.ORM.annotation.Table) annotation;
				String name = tableanno.name();
				String catalog = tableanno.catalog();
				String schema = tableanno.schema();
				UniqueConstraint[] unique = tableanno.uniqueConstraints();
				result.setName(name==""?clazz.getSimpleName():name);
			}
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			Annotation[] fieldanno = field.getDeclaredAnnotations();
			for (Annotation annotation : fieldanno) {
				Class type = annotation.annotationType();
				if(type==name.luojun.DBhelper.ORM.annotation.Column.class){
					name.luojun.DBhelper.ORM.annotation.Column column=(name.luojun.DBhelper.ORM.annotation.Column)annotation;
					column.comment();
				}else if(type==name.luojun.DBhelper.ORM.annotation.Id.class){
					
				}
			}
		}
		if (casesensitive) {
		} else {
		}
		return null;
	}
	public void fillInTable(Annotation annotation,Table result){
		Class type = annotation.annotationType();
		if (type == name.luojun.DBhelper.ORM.annotation.Table.class) {
			name.luojun.DBhelper.ORM.annotation.Table tableanno = (name.luojun.DBhelper.ORM.annotation.Table) annotation;
			String name = tableanno.name();
			String catalog = tableanno.catalog();
			String schema = tableanno.schema();
			UniqueConstraint[] unique = tableanno.uniqueConstraints();
			result.setName(name==""?result.getClazz().getSimpleName():name);
		}else if(type==name.luojun.DBhelper.ORM.annotation.Column.class){
			name.luojun.DBhelper.ORM.annotation.Column column=(name.luojun.DBhelper.ORM.annotation.Column)annotation;
			column.comment();
		}else if(type==name.luojun.DBhelper.ORM.annotation.Id.class){
			
		}
		
	}
	public static AnnotationTableManager getInstance() {
		return instance;
	}
	public String[] getPackagesToScan() {
		return packagesToScan;
	}
	public void setPackagesToScan(String[] packagesToScan) {
		this.packagesToScan = packagesToScan;
	}
	public String getDialect() {
		return dialect;
	}
	public void setDialect(String dialect) {
		this.dialect = dialect;
	}
	private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
		if (entityTypeFilters != null) {
			TypeFilter arr$[] = entityTypeFilters;
			int len$ = arr$.length;
			for (int i$ = 0; i$ < len$; i$++) {
				TypeFilter filter = arr$[i$];
				if (filter.match(reader, readerFactory))
					return true;
			}
		}
		return false;
	}
	private TypeFilter entityTypeFilters[] = {new AnnotationTypeFilter(name.luojun.DBhelper.ORM.annotation.Table.class, false)};
}
